Skip to content

Experiment: does --hidden-import=gribapi bundle eccodes.dll for pygrib? - #415

Merged
mixmansoundude merged 3 commits into
mainfrom
claude/gribapi-hidden-import-probe
Aug 4, 2026
Merged

Experiment: does --hidden-import=gribapi bundle eccodes.dll for pygrib?#415
mixmansoundude merged 3 commits into
mainfrom
claude/gribapi-hidden-import-probe

Conversation

@mixmansoundude

Copy link
Copy Markdown
Owner

Summary

Standalone, isolated CI experiment for CLAUDE.md Active Backlog Item 24 / docs/prd-conda-native-dll-bundling.md Requirement 1 (currently on the separate, not-yet-merged PR #414). Deliberately kept its own PR per explicit owner instruction, so this cheap "does the free lunch exist" question doesn't get entangled with the larger repair-loop implementation.

  • New tests/selfapps_gribapi_hook_probe.ps1: does not invoke run_setup.bat at all. Creates its own scratch conda env (python pygrib eccodes python-eccodes pyinstaller pip --override-channels -c conda-forge), then builds a trivial import pygrib stub with PyInstaller twice — once as a control, once with --hidden-import gribapi added — and records whether eccodes.dll ends up bundled and whether each resulting EXE actually runs clean.
  • Wired as a new step in the conda-full lane in batch-check.yml, gated on the existing steps.conda_avail.outputs.available == 'true' check (same gate the other 27 conda-full-only self-tests use), continue-on-error: true (exploratory — the three-way conda-forge solve is unproven, and the research finding this test is checking is expected to come back negative).
  • docs/agent-ndjson.md gets the new row registered (self.gribapi_hook_probe.hidden_import) with the full evidence/assertion writeup.

Expected outcome, stated up front so a negative result isn't surprising: research in the PRD (reading hook-gribapi.py's actual source, and confirming pygrib/gribapi are architecturally independent bindings to the same eccodes C library) suggests this will most likely come back negative — pygrib never imports gribapi, so PyInstaller's hook has no reason to fire. Still worth the real CI evidence per explicit owner instruction rather than relying on analysis alone. pass in the emitted NDJSON row reflects whether the experiment ran to completion and produced conclusive evidence, not whether the hidden-import "worked" — the actual finding is recorded in details.hiddenImportHelped.

Test plan

  • python tools/check_delimiters.py tests/selfapps_gribapi_hook_probe.ps1 — clean.
  • PowerShell AST parse (pwsh -NoProfile -c "[...]::ParseFile(...)") — clean.
  • actionlint -oneline .github/workflows/batch-check.yml — clean.
  • Full tools/run_sanity_sweep.sh — all checks pass, 472 pytest passed / 2 skipped.
  • Real CI: the conda-full lane's new step is the actual test of this PR — will report back once it lands (est. slow, since it does its own conda solve + two PyInstaller builds inside an already-long lane).

Depends on nothing else landing first; can merge independently of PR #414.


Generated by Claude Code

…or pygrib?

Standalone, non-gating probe (does not invoke run_setup.bat) testing PRD
Requirement 1's cheapest-option-first question: create a scratch conda env
with pygrib+eccodes+python-eccodes+pyinstaller, build a trivial pygrib
stub twice (control vs --hidden-import=gribapi), and record whether the
existing pyinstaller-hooks-contrib hook-gribapi.py bundles eccodes.dll for
free. Research (docs/prd-conda-native-dll-bundling.md Finding 1) suggests
this is likely negative since pygrib and gribapi are independent bindings
to the same C library, but the owner asked for real CI evidence before
ruling it out. Wired as its own step in the conda-full lane, gated on the
existing conda-availability check, continue-on-error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mixmansoundude, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6a590b45-446b-4516-8558-5eaa9c8fa389

📥 Commits

Reviewing files that changed from the base of the PR and between 8c7463e and 87a678a.

📒 Files selected for processing (2)
  • docs/agent-ndjson.md
  • tests/selfapps_gribapi_hook_probe.ps1
📝 Walkthrough

Summary by CodeRabbit

  • Tests
    • Added a Windows-only diagnostic probe to compare PyInstaller builds with and without the gribapi hidden import.
    • Reports bundled DLL detection, executable launch results, and diagnostic outcomes in NDJSON format.
    • Runs non-blockingly in the conda-full workflow when Miniconda is available.

Walkthrough

Changes

GRIB-API hook probe

Layer / File(s) Summary
Probe setup and environment
tests/selfapps_gribapi_hook_probe.ps1
The script validates Windows and Miniconda availability, creates a temporary conda environment, prepares a minimal pygrib application, and initializes NDJSON reporting.
Control and hidden-import comparison
tests/selfapps_gribapi_hook_probe.ps1
The script builds control and --hidden-import=gribapi variants, checks DLL bundling and executable runtime results, determines conclusiveness, and records the result.
CI registration and probe documentation
.github/workflows/batch-check.yml, docs/agent-ndjson.md
The conda-full workflow runs the non-gating probe when Miniconda is available. Documentation describes its registry row, procedure, and result semantics.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI as conda-full CI
  participant Probe as PowerShell probe
  participant Conda as conda environment
  participant PyInstaller
  participant EXE as built executable
  participant NDJSON as NDJSON results
  CI->>Probe: Invoke probe when Miniconda is available
  Probe->>Conda: Create environment and sample app
  Probe->>PyInstaller: Build control variant
  PyInstaller-->>Probe: Return build output and executable
  Probe->>EXE: Run control executable
  Probe->>PyInstaller: Build variant with --hidden-import gribapi
  PyInstaller-->>Probe: Return build output and executable
  Probe->>EXE: Run experiment executable
  Probe->>NDJSON: Record DLL, runtime, and conclusiveness results
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the isolated experiment and its primary PyInstaller bundling objective.
Description check ✅ Passed The description accurately explains the probe, CI integration, expected outcome, and test plan.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/gribapi-hidden-import-probe

Comment @coderabbitai help to get the list of available commands.

@mixmansoundude
mixmansoundude enabled auto-merge (squash) August 4, 2026 13:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/batch-check.yml:
- Around line 997-1002: Update the NDJSON verdict and enforcement flow for
self.gribapi_hook_probe.hidden_import so its setup or inconclusive failures do
not fail the conda-full lane, while retaining the probe row in artifacts and the
Job Summary. Prefer excluding this specific probe from the gating verdict;
otherwise adjust its exploratory output to avoid pass=false without affecting
other probes.

In `@tests/selfapps_gribapi_hook_probe.ps1`:
- Line 109: Rename the temporary log filenames assigned near $envCreateLog and
the corresponding build log variable to use the required tilde prefix, such as
~env_create.log and ~build_$Variant.log, while preserving the existing paths and
logging behavior.
- Around line 189-216: Update the runtime observation logic around the EXE
execution block and the later `$conclusive` calculation so each probe records
whether the process launched, completed within the timeout, and had both output
streams captured. Set `$conclusive` only when both EXE observations satisfy all
completion and capture conditions; keep timed-out processes, launch exceptions,
and unreadable output inconclusive rather than deriving pass or
hiddenImportHelped results from them.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dbe04c65-7f8f-4471-a75a-d38515cd22ce

📥 Commits

Reviewing files that changed from the base of the PR and between 4c870e4 and 4aec696.

📒 Files selected for processing (3)
  • .github/workflows/batch-check.yml
  • docs/agent-ndjson.md
  • tests/selfapps_gribapi_hook_probe.ps1
📜 Review details
⏰ Context from checks skipped due to timeout. (9)
  • GitHub Check: auto_merge
  • GitHub Check: Batch syntax/run check (contract-uv-fail)
  • GitHub Check: Batch syntax/run check (uv)
  • GitHub Check: Batch syntax/run check (cache)
  • GitHub Check: Batch syntax/run check (real)
  • GitHub Check: Batch syntax/run check (uv-dl-fallback)
  • GitHub Check: Batch syntax/run check (conda-full)
  • GitHub Check: Batch syntax/run check (justme-test)
  • GitHub Check: Batch syntax/run check (contract-uv)
🧰 Additional context used
📓 Path-based instructions (12)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

The single-bootstrapper directive requires run_setup.bat to work when dropped next to the application without committed helper files; test-only helpers may live under tests/ but cannot be required by the real flow.

Files:

  • docs/agent-ndjson.md
  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{md,txt}

📄 CodeRabbit inference engine (AGENTS.md)

When documenting run_setup.bat, cite labels or subroutine names rather than exact line numbers unless a line number provides immediate value for the introducing commit.

Files:

  • docs/agent-ndjson.md
docs/agent-*.md

📄 CodeRabbit inference engine (CLAUDE.md)

When a change teaches or invalidates a lesson, update the relevant knowledge document in the same commit, editing existing entries rather than only appending. Move resolved Active Backlog items to docs/agent-closed-backlog.md.

Files:

  • docs/agent-ndjson.md
**/.github/workflows/*.{yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

**/.github/workflows/*.{yml,yaml}: Use GitHub Actions CI results, especially Job Summary, grouped log tails, and workflow PR failure comments, as the source of truth; local runs are advisory.
Isolate slow, flaky, or environment-dependent diagnostics in separate non-gating lanes using continue-on-error rather than weakening deterministic gating lanes.
Keep CI parser checks synchronized with exact bootstrapper messages and preserve the single parser-facing iterate signal * Iterate logs: {found|missing}.
The iterate job must upload one artifact named iterate-logs-${run_id}-${run_attempt} containing iterate/_temp/ and the job summary; do not alter the intentional pre-flight gate failure for missing NDJSON inputs.

Files:

  • .github/workflows/batch-check.yml
.github/workflows/*.yml

📄 CodeRabbit inference engine (CLAUDE.md)

Validate modified GitHub Actions workflows with both actionlint and YAML syntax/lint checks before committing.

Files:

  • .github/workflows/batch-check.yml
**/*.{ps1,psm1,psd1}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ps1,psm1,psd1}: Prepend TLS 1.2 configuration to every PowerShell 5.1 Invoke-WebRequest call and retain -UseBasicParsing.
Validate touched PowerShell files with PowerShell AST parsing; do not skip validation on Linux, and run modified scripts directly with realistic environment variables when practical.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{bat,cmd,ps1,py,yml,yaml,json}

📄 CodeRabbit inference engine (AGENTS.md)

Run delimiter and repository-specific syntax regression checks using tools/check_delimiters.py, respecting comments, escapes, and here-strings as applicable.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{py,ps1,psm1,psd1,yml,yaml,json,bat,cmd}

📄 CodeRabbit inference engine (AGENTS.md)

Keep source text ASCII plain text and avoid non-ASCII punctuation.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{bat,ps1}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{bat,ps1}: Use ASCII-only content; avoid emojis, curly quotes, em-dashes, and other non-ASCII characters because Windows batch and PowerShell parsing can break.
Keep .bat and .ps1 files in CRLF format; other files use LF, as controlled by .gitattributes.
Avoid EnableDelayedExpansion in batch scripts; if required, scope it tightly.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{bat,ps1,py}

📄 CodeRabbit inference engine (CLAUDE.md)

Use tilde-prefixed temporary files such as ~setup.log and ~bootstrap.status.json so they are easy to ignore and survive crashes.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.ps1

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ps1: Use PowerShell -or and -and only in valid conditional expressions; outside conditionals they may be parsed as parameter names.
Run a PowerShell parser sweep over modified PowerShell scripts before committing.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
tests/selfapps_*.ps1

📄 CodeRabbit inference engine (CLAUDE.md)

PowerShell scenario tests belong in tests/selfapps_<scenario>.ps1; wire new scenarios into tests/harness.ps1 and the CI job steps.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
🪛 PSScriptAnalyzer (1.25.0)
tests/selfapps_gribapi_hook_probe.ps1

[warning] 172-172: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 176-176: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 177-177: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 204-204: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 208-208: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 209-209: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)

Comment thread .github/workflows/batch-check.yml
Comment thread tests/selfapps_gribapi_hook_probe.ps1 Outdated
Comment thread tests/selfapps_gribapi_hook_probe.ps1 Outdated
- Real bug: continue-on-error on the CI step does NOT make this probe
  non-gating -- batch-check.yml's separate "Verdict from NDJSON" +
  "Enforce NDJSON failures" steps scan raw pass values across ALL rows and
  fail the conda-full lane job on any pass=false, independent of which
  step emitted it. This diagnostic/exploratory probe must never gate, so
  every exit path now emits pass=true (skip=true or details.conclusive
  carries the real outcome) and exits 0 unconditionally.
- Tighten "conclusive evidence": track buildLaunched/buildCompleted/
  buildOutputCaptured and exeLaunched/exeCompleted/exeOutputCaptured
  explicitly per variant, so a timed-out process, launch exception, or
  unreadable output can no longer masquerade as a real (negative) finding
  via the old -1/empty-string defaults. observationComplete now requires
  all three EXE-side conditions before ranClean is trusted.
- Tilde-prefix the temp log files (~env_create.log, ~build_$Variant.log)
  per repo convention.
- Add Write-Warning to the two taskkill catch blocks (matching
  selfapps_layered_e2e.ps1's own established precedent); the remaining
  empty catches are the same "best-effort .Result read after a bounded
  Wait()" pattern already accepted in that file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/selfapps_gribapi_hook_probe.ps1 (1)

132-141: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Clean up the created environment before this skip.

When this guard runs, conda create already returned success. This branch exits before the post-create cleanup at Line 285. gribapi_probe_env can remain on the runner.

Run the same conda env remove command before this exit, or use one cleanup helper for every post-create exit.

Suggested cleanup
 if (-not (Test-Path -LiteralPath $envPython)) {
+    & cmd /c "call `"$condaBat`" env remove -y -n $envName >nul 2>&1"
     Write-NdjsonRow ([ordered]@{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/selfapps_gribapi_hook_probe.ps1` around lines 132 - 141, The early exit
at line 141 (the `exit 0` statement in the Test-Path guard block) bypasses the
post-create cleanup logic that removes the conda environment at line 285,
leaving gribapi_probe_env on the runner. Add the same conda environment removal
command before this early `exit 0` to ensure the environment is cleaned up when
the python.exe missing condition is detected, or create a shared cleanup helper
function that runs before every post-create exit path in the script.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/selfapps_gribapi_hook_probe.ps1`:
- Around line 260-261: Update the conclusive evidence gate around
$observationComplete, $ranClean, and $conclusive to define a successful build
observation requiring buildCompleted, buildOutputCaptured, and a successful
buildExit, then require that condition for both variants before reporting
conclusive evidence. Do not treat the executable's existence or launch results
as conclusive when the build timed out or failed.
- Around line 192-197: Update both timeout termination branches around the
taskkill.exe calls to capture $LASTEXITCODE immediately after invocation, then
call $proc.WaitForExit(5000) and treat a nonzero exit code or false result as
termination failure. Preserve the existing warning behavior while ensuring both
branches verify that the process actually exited.
- Around line 287-295: Change the PyInstaller build configuration from --onefile
to --onedir so DLLs are written to the filesystem under $distDir where they can
be detected by the scan. Update $exePath to point to the binary within the
variant subdirectory. Replace the $hiddenImportHelped calculation to measure DLL
bundling using $bundledDll instead of runtime cleanliness with $ranClean,
specifically using (-not $control.bundledDll) -and $experiment.bundledDll to
determine if the hidden-import helped the bundling outcome.

---

Outside diff comments:
In `@tests/selfapps_gribapi_hook_probe.ps1`:
- Around line 132-141: The early exit at line 141 (the `exit 0` statement in the
Test-Path guard block) bypasses the post-create cleanup logic that removes the
conda environment at line 285, leaving gribapi_probe_env on the runner. Add the
same conda environment removal command before this early `exit 0` to ensure the
environment is cleaned up when the python.exe missing condition is detected, or
create a shared cleanup helper function that runs before every post-create exit
path in the script.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 89e78f14-8138-401b-8cc5-2f4d8f17c9a4

📥 Commits

Reviewing files that changed from the base of the PR and between 4aec696 and 8c7463e.

📒 Files selected for processing (1)
  • tests/selfapps_gribapi_hook_probe.ps1
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

The single-bootstrapper directive requires run_setup.bat to work when dropped next to the application without committed helper files; test-only helpers may live under tests/ but cannot be required by the real flow.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{ps1,psm1,psd1}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ps1,psm1,psd1}: Prepend TLS 1.2 configuration to every PowerShell 5.1 Invoke-WebRequest call and retain -UseBasicParsing.
Validate touched PowerShell files with PowerShell AST parsing; do not skip validation on Linux, and run modified scripts directly with realistic environment variables when practical.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{bat,cmd,ps1,py,yml,yaml,json}

📄 CodeRabbit inference engine (AGENTS.md)

Run delimiter and repository-specific syntax regression checks using tools/check_delimiters.py, respecting comments, escapes, and here-strings as applicable.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{py,ps1,psm1,psd1,yml,yaml,json,bat,cmd}

📄 CodeRabbit inference engine (AGENTS.md)

Keep source text ASCII plain text and avoid non-ASCII punctuation.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{bat,ps1}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{bat,ps1}: Use ASCII-only content; avoid emojis, curly quotes, em-dashes, and other non-ASCII characters because Windows batch and PowerShell parsing can break.
Keep .bat and .ps1 files in CRLF format; other files use LF, as controlled by .gitattributes.
Avoid EnableDelayedExpansion in batch scripts; if required, scope it tightly.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.{bat,ps1,py}

📄 CodeRabbit inference engine (CLAUDE.md)

Use tilde-prefixed temporary files such as ~setup.log and ~bootstrap.status.json so they are easy to ignore and survive crashes.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
**/*.ps1

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ps1: Use PowerShell -or and -and only in valid conditional expressions; outside conditionals they may be parsed as parameter names.
Run a PowerShell parser sweep over modified PowerShell scripts before committing.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
tests/selfapps_*.ps1

📄 CodeRabbit inference engine (CLAUDE.md)

PowerShell scenario tests belong in tests/selfapps_<scenario>.ps1; wire new scenarios into tests/harness.ps1 and the CI job steps.

Files:

  • tests/selfapps_gribapi_hook_probe.ps1
🪛 PSScriptAnalyzer (1.25.0)
tests/selfapps_gribapi_hook_probe.ps1

[warning] 200-200: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 201-201: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 246-246: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)


[warning] 247-247: Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

(PSAvoidUsingEmptyCatchBlock)

Comment thread tests/selfapps_gribapi_hook_probe.ps1
Comment thread tests/selfapps_gribapi_hook_probe.ps1 Outdated
Comment thread tests/selfapps_gribapi_hook_probe.ps1
- Critical: switch --onefile to --onedir. --onefile embeds all bundled
  support files (DLLs included) inside the compressed EXE, extracting
  them only to a runtime-only _MEIxxxxxx temp directory that never
  touches disk under the dist path -- the bundledDll filesystem scan
  could therefore NEVER detect a positive result under --onefile,
  regardless of whether the hidden-import actually worked. This was a
  structural flaw in the experiment itself, not a finding about the
  research question. --onedir writes bundled files under
  dist_$Variant\probe_$Variant\, where the scan can see them; $exePath
  updated to point into that subdirectory.
- hiddenImportHelped now derived from bundledDll (a structural,
  build-output fact) instead of ranClean (a runtime-behavior fact that
  conflates "did it bundle" with "did it happen to run cleanly for any
  reason"); ranClean stays recorded per-variant as supplementary
  evidence.
- Add buildObservationComplete (build launched, completed within budget,
  output captured, exited 0) as a prerequisite for observationComplete,
  so a build that timed out or left a partial EXE on disk before being
  killed can no longer be treated as conclusive evidence.
- Clean up the scratch conda env before the python.exe-missing early
  exit, matching every other post-create exit path.
- Verify taskkill.exe actually terminated the process (check
  $LASTEXITCODE + a bounded WaitForExit) instead of assuming success
  whenever the call itself didn't throw.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW
@mixmansoundude
mixmansoundude merged commit 79b1df9 into main Aug 4, 2026
16 checks passed
@mixmansoundude
mixmansoundude deleted the claude/gribapi-hidden-import-probe branch August 4, 2026 15:24
mixmansoundude pushed a commit that referenced this pull request Aug 4, 2026
mixmansoundude pushed a commit that referenced this pull request Aug 4, 2026
…s 2-6)

Requirement 1's own CI experiment (PR #415, self.gribapi_hook_probe.hidden_import)
confirmed --hidden-import=gribapi does not make hook-gribapi.py bundle eccodes.dll
for a pygrib-only build, ruling out the free-lunch shortcut. This adds the actual
repair loop: :dll_bundle_recover in run_setup.bat reacts to PyInstaller's own
build-time "Library not found: could not resolve" warning (before the smoke run,
skipping a guaranteed-failing verification cycle), mirrors :hidden_import_recover's
bounded/tried-list shape, is gated to HP_ENV_MODE=conda for the actual --add-binary
action, and carries the same HP_NUITKA_FALLBACK_USED guard. Built general (parses
whatever DLL name PyInstaller names, not hardcoded to eccodes.dll) rather than
narrow, resolving the open question in docs/open-questions.md.

tools/dll_bundle_scan.py is the new scanning helper (HP_DLL_BUNDLE_SCAN payload),
double-gated like its sibling: a named DLL must actually exist under the conda
env's Library\bin (searched recursively) before anything is emitted. 24 unit
tests in tests/test_dll_bundle_scan.py.

tests/selfapps_layered_e2e.ps1 gains a 4th mechanism (mech4Pass) alongside the
three it already proves for real -- pygrib's conda-forge build genuinely
triggers this exact DLL-bundling gap, so this is the loop's own Requirement 4
regression test. Not yet confirmed in real CI (no Windows/conda available in
this sandbox); flagged as such in CLAUDE.md's Item 24 entry pending a real
cache-lane run.

Also merges origin/main (PR #415) to pick up the gribapi probe test and its
docs/agent-ndjson.md registration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW
mixmansoundude pushed a commit that referenced this pull request Aug 4, 2026
Real Windows-CI failure (all 8 lanes): tests/test_dll_bundle_scan.py wrote
fixture files via Path.write_text(), which translates \n to \r\n on Windows
but not Linux -- exact-string assertions in ReadTail passed locally and
failed on the real runner. Fixed by switching every fixture write to
Path.write_bytes(text.encode("utf-8")), which never translates.

Four genuine bugs caught by CodeRabbit review in the first-shipped
:dll_bundle_recover, all fixed:
- Detection itself was gated on HP_ENV_MODE=conda, defeating the PRD's own
  "provider-agnostic detection" design. Restructured so a --detect-mode
  scan runs first and unconditionally; the conda gate and the Nuitka guard
  now each log "detected but can't repair" instead of staying silent.
- The tried-list was an unquoted batch variable expanded on the scan
  command line -- a DLL basename containing a space or & would corrupt or
  split it. Replaced with a file (~dll_bundle_tried.txt, appended via
  type >>, never through %VAR%-expanded command text).
- main() stopped at the first candidate that failed the Library\bin
  lookup instead of trying the next one. Fixed with a loop that skips
  unresolvable candidates.
- The "bundling complete" log line could fire after a genuine rebuild
  failure, since HP_DLL_ITER GEQ 1 is true on both the success and
  failure paths. Fixed with an explicit HP_DLL_FAILED flag (the same bug
  class already fixed once before for the warnfix-triggered rebuild).

tools/dll_bundle_scan.py gained --detect mode, read_tried_file(), and the
continue-past-unresolvable loop, with 10 new unit tests (34 total). PRD
status text updated from "expected negative" to the confirmed PR #415
result, and Requirement statuses reconciled to a single consistent state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW
mixmansoundude added a commit that referenced this pull request Aug 8, 2026
…tion) (#414)

* Item 24: confirm hook-gribapi.py source, thaw PRD, doc/tooling research

Reads pyinstaller-hooks-contrib's actual hook-gribapi.py source and confirms
pygrib and gribapi are architecturally independent bindings to eccodes, so
the "does an existing hook solve this for free" question is now assessed as
likely-negative (still worth the cheap empirical CI test). Adds a Nuitka
interaction finding/requirement (the new repair loop needs the same
HP_NUITKA_FALLBACK_USED guard :hidden_import_recover already has; the
elective optimized-build upsell doesn't "fall back" on failure, it just
doesn't swap). Thaws the PRD from cold storage per owner instruction and
moves active tracking back to CLAUDE.md's Item 24. Files an open question
about extending the post-flight caveat panel with a DLL-specific hint once
the repair loop's own detection signal exists.

Also: corrects a stale AGENTS.md claim that PSGallery is blocked by proxy
(disproved directly -- PSScriptAnalyzer installs and runs cleanly), and
records research on both PSScriptAnalyzer and Blinter as sanity-sweep
candidates -- neither adopted yet, both produce heavy false-positive noise
against this repo's own deliberate conventions without a real per-rule
audit first (documented as Cold Storage candidates with concrete thaw
triggers).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Fix CodeRabbit review findings on Item 24 docs

- Separate the PRD's proven fact (hook-gribapi.py won't auto-fire for a
  pygrib-only import) from the still-unverified expectation (forcing
  --hidden-import=gribapi likely won't help either) -- the two were
  conflated in the prior draft; Requirement 1's CI experiment is the
  actual decision point, not this section's reasoning.
- Fix "Tier 9" -> "Requirement 9" naming (Tier A is reserved for the
  Nuitka AV-Safe Build Path).
- Note conda-forge-only channel enforcement explicitly in Requirement 1's
  text (already correctly implemented in the experiment PR itself).
- Rework the open-questions.md caveat-panel entry to distinguish
  detected-and-skipped (non-conda provider or Nuitka build, per
  Requirements 3/6) from attempted-and-failed/succeeded -- the original
  wording conflated "detection fired" with "repair was attempted."
- Trim the Blinter/PSScriptAnalyzer cold-storage entries to reference
  AGENTS.md's own findings instead of duplicating them, per this repo's
  own "don't duplicate content across docs" convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Implement conda native-DLL bundling repair loop (Item 24, Requirements 2-6)

Requirement 1's own CI experiment (PR #415, self.gribapi_hook_probe.hidden_import)
confirmed --hidden-import=gribapi does not make hook-gribapi.py bundle eccodes.dll
for a pygrib-only build, ruling out the free-lunch shortcut. This adds the actual
repair loop: :dll_bundle_recover in run_setup.bat reacts to PyInstaller's own
build-time "Library not found: could not resolve" warning (before the smoke run,
skipping a guaranteed-failing verification cycle), mirrors :hidden_import_recover's
bounded/tried-list shape, is gated to HP_ENV_MODE=conda for the actual --add-binary
action, and carries the same HP_NUITKA_FALLBACK_USED guard. Built general (parses
whatever DLL name PyInstaller names, not hardcoded to eccodes.dll) rather than
narrow, resolving the open question in docs/open-questions.md.

tools/dll_bundle_scan.py is the new scanning helper (HP_DLL_BUNDLE_SCAN payload),
double-gated like its sibling: a named DLL must actually exist under the conda
env's Library\bin (searched recursively) before anything is emitted. 24 unit
tests in tests/test_dll_bundle_scan.py.

tests/selfapps_layered_e2e.ps1 gains a 4th mechanism (mech4Pass) alongside the
three it already proves for real -- pygrib's conda-forge build genuinely
triggers this exact DLL-bundling gap, so this is the loop's own Requirement 4
regression test. Not yet confirmed in real CI (no Windows/conda available in
this sandbox); flagged as such in CLAUDE.md's Item 24 entry pending a real
cache-lane run.

Also merges origin/main (PR #415) to pick up the gribapi probe test and its
docs/agent-ndjson.md registration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Update PRD status header to reflect implementation (Requirements 1-3/5/6)

Fixes a markdown bug in the same edit: a multi-line "comment" using
repeated ## prefixes would have rendered as three separate H2 headings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Fix CI failure + address CodeRabbit review round on DLL bundling loop

Real Windows-CI failure (all 8 lanes): tests/test_dll_bundle_scan.py wrote
fixture files via Path.write_text(), which translates \n to \r\n on Windows
but not Linux -- exact-string assertions in ReadTail passed locally and
failed on the real runner. Fixed by switching every fixture write to
Path.write_bytes(text.encode("utf-8")), which never translates.

Four genuine bugs caught by CodeRabbit review in the first-shipped
:dll_bundle_recover, all fixed:
- Detection itself was gated on HP_ENV_MODE=conda, defeating the PRD's own
  "provider-agnostic detection" design. Restructured so a --detect-mode
  scan runs first and unconditionally; the conda gate and the Nuitka guard
  now each log "detected but can't repair" instead of staying silent.
- The tried-list was an unquoted batch variable expanded on the scan
  command line -- a DLL basename containing a space or & would corrupt or
  split it. Replaced with a file (~dll_bundle_tried.txt, appended via
  type >>, never through %VAR%-expanded command text).
- main() stopped at the first candidate that failed the Library\bin
  lookup instead of trying the next one. Fixed with a loop that skips
  unresolvable candidates.
- The "bundling complete" log line could fire after a genuine rebuild
  failure, since HP_DLL_ITER GEQ 1 is true on both the success and
  failure paths. Fixed with an explicit HP_DLL_FAILED flag (the same bug
  class already fixed once before for the warnfix-triggered rebuild).

tools/dll_bundle_scan.py gained --detect mode, read_tried_file(), and the
continue-past-unresolvable loop, with 10 new unit tests (34 total). PRD
status text updated from "expected negative" to the confirmed PR #415
result, and Requirement statuses reconciled to a single consistent state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Fix CMD-injection risk in DLL bundle logging; add dedicated NDJSON row

Two CodeRabbit findings on the DLL bundling repair loop's first review round:

- HP_DLL_DETECTED/HP_NEXT_DLL/HP_NEXT_DLL_PATH (derived from PyInstaller's own
  build-log warning text) can legally contain &/|/</> on Windows, which :log's
  unquoted echo would reinterpret as live redirection/pipe operators. Fixed
  with display-only _SAFE sanitized variables used only in :log calls; every
  functional use of the raw value (tried-file byte-copy, quoted --add-binary
  argument) is untouched, so tried-list dedup matching cannot desync.

- The loop's detected/skipped/repaired/unlocatable/failed outcomes previously
  reached only :log's console text, with no machine-readable record. Added a
  shared :emit_dll_bundle_row subroutine, called from all 6 outcome points,
  emitting NDJSON id self.dll_bundle.recover. The DLL name/provider/iteration
  are pulled inside the emitting PowerShell command via
  [Environment]::GetEnvironmentVariable(...) rather than %VAR% cmd.exe
  substitution, protecting cmd.exe's own command-line parsing the same way
  the _SAFE variables protect :log's echo.

tests/harness.ps1 gains a static wiring check (batch.dll_bundle.ndjson) since
this row is not observable in self.layered_e2e.chain's own artifact (that
test's isolated sub-bootstrap leaves HP_NDJSON unset, matching the existing
convention in selfapps_postexec_checkpoint.ps1). docs/agent-interconnect.md,
docs/agent-ndjson.md, and CLAUDE.md's Item 24 entry updated accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Strip %/^ from DLL bundle log sanitization (call's second expansion pass)

A third CodeRabbit review pass caught a real gap in the just-shipped _SAFE
display-only sanitization: it stripped &/|/</> but not % or ^. call :log
"... %VAR_SAFE% ..." triggers cmd.exe's own second expansion pass on its
already-substituted command line (a well-established call behavior), so a
raw % surviving into a _SAFE variable could still expand an unrelated
environment variable into the log if the underlying DLL name happened to be
shaped like %SOME_VAR% (regex-extracted from arbitrary PyInstaller warning
text, so not restricted to real filename characters the way HP_NEXT_DLL_PATH
otherwise would be).

Extended all three _SAFE chains (HP_DLL_DETECTED_SAFE/HP_NEXT_DLL_SAFE/
HP_NEXT_DLL_PATH_SAFE) with two more substitutions each: % (doubled to %% in
the search text, matching a literal percent sign) and ^ (no doubling needed).
Documented as a standalone lesson in docs/agent-lessons-learned.md (the
call-triggers-a-second-expansion-pass mechanism is a general cmd.exe hazard,
not specific to this one loop) and in docs/agent-interconnect.md's DLL
bundling section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Fix cascade version-pin bug blocking self.layered_e2e.chain; add CI-verified percent-sanitizer test

Real CI evidence (self.layered_e2e.chain, cache lane, 2026-08-07): chainPass came
back false, but the failure was earlier than the DLL-bundling loop entirely --
mech1Pass:false, condaSelected:false, statusState:"venv_env". Root cause traced
via the real ~setup.log/~layered_e2e_bootstrap.log artifacts: the runtime.txt
write-back feature (REQ-004) pins the EXACT patch version uv resolved (e.g.
python-3.14.7) the moment uv's own venv succeeds. When the REQ-009 cascade later
re-enters targeting conda (pygrib still failing to build under uv),
:after_env_mode_selection's re-derivation of PYSPEC reads that freshly-written
runtime.txt and forwards python=3.14.7 verbatim to conda create -- but
conda-forge's own python package release cadence is a separate index from
CPython's/uv's and did not have that exact patch, producing
PackagesNotFoundInChannelsError and a hard conda env create failure. The chain
fell through embed -> venv, never reaching a real conda environment, so the
DLL-bundling loop itself was never exercised in that run.

Fixed with a new HP_PYSPEC_WRITEBACK flag, set at all 3 sites where write-back
reassigns PYSPEC, marking that value as self-authored by whichever provider ran
first rather than a genuine user requirement. :try_conda_create now drops the
version pin (falls back to its existing unconstrained conda create branch)
whenever HP_PYSPEC_WRITEBACK is set, at both the initial attempt and the
transient retry -- a real pre-existing user pin (HP_RUNTIME_TXT_PREEXIST
defined) is left untouched. selfapps_layered_e2e.ps1 gains a pinDropped
visibility field to confirm the fix's own code path fires.

Also settles a CodeRabbit/Blinter disagreement empirically: Blinter flagged the
%VAR:%%=_% doubled-percent sanitization idiom (from the prior commit's CMD-
injection fix) as a possible malformed string operation, since its static
pattern matcher does not model the doubled-%% escape for a literal percent sign
inside a :search=replace substitution. Rather than trust either static tool or
further reasoning about undocumented cmd.exe parsing, tests/harness.ps1 gains a
new gating check (batch.dll_bundle.pct_sanitizer) that runs the exact idiom
against a real cmd.exe via a live fixture and asserts the observed output.

Deferred to a new CLAUDE.md Active Backlog Item 25 (not fixed here, needs its
own focused loop): a genuine but low-trigger-rate correctness gap where the
DLL-bundle loop reports "repaired" instead of a distinct "exhausted" outcome
when a 4th locatable DLL candidate is found after the 3-iteration cap is
already hit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Fix broken percent-sanitizer idiom; preserve user Python constraints on cascade

The %VAR:%%=_% cmd.exe substitution shipped in the previous commit was
confirmed broken by real Windows CI (silently produced an empty string
instead of stripping a literal percent sign). Replaced the % and ^
stripping in the DLL-bundle loop's _SAFE sanitization with a PowerShell
-replace call instead, and rewrote tests/harness.ps1's
batch.dll_bundle.pct_sanitizer fixture to validate the real mechanism
(plus a call-based second-expansion-pass leak check) instead of the
abandoned idiom.

Also adds HP_PYSPEC_ORIGINAL, snapshotted at each runtime.txt write-back
site before PYSPEC is overwritten, so a REQ-009 provider cascade re-entry
into conda preserves a genuine user-authored pyproject.toml/PEP 723
requires-python range instead of always dropping to no constraint.
Fixes a related pre-existing bug found in the process: %PYSPEC% was used
unquoted on both conda create command lines, so a PEP 440 range
containing < or > would corrupt the command via cmd.exe redirection
parsing.

Corrects the docs that had described the broken %%=_% idiom as a
standard cmd.exe pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Correct Windows filename claim about & in agent-interconnect.md

& is legal in NTFS filenames (only < > : " / \ | ? * are forbidden);
the DLL-bundle sanitization strips it because it's a CMD transport
hazard once the path reaches an unquoted :log echo, not because it's
filesystem-illegal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Register batch.dll_bundle.pct_sanitizer in the NDJSON row registry

Real, non-stale finding from the advisory NDJSON registry cross-check
CI job: this row (added earlier in the DLL-bundling percent-sanitizer
fix) was emitted by tests/harness.ps1 but never registered in
docs/agent-ndjson.md, violating the repo's own AGENT DIRECTIVE.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Add Item 26: ENVNAME sanitization loses readability on & in folder names

Owner-suggested refinement (general guidance, not a bug report): the
built EXE's filename (dist\%ENVNAME%.exe) already sanitizes & safely
(blanket [^A-Za-z0-9_-] -> _), but collapsing it to an unreadable
underscore instead of "and" loses meaning for a file a user might
rename and email. Deferred as a far-term nice-to-have per its own
framing -- no live safety issue.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Fix odd-percent-pairing bug in DLL-bundle _SAFE sanitization PowerShell calls

Real CI failure across 5+ lanes (identical "ECHO is off." signature as
the earlier %%=_% bug) confirmed the PowerShell -replace fix shipped
in the prior commit was itself broken: a lone, unpaired % literal in
`-replace '%','_'` sat on the same cmd.exe logical line as %LOG%/
%TEMP%. cmd.exe pairs % characters via a left-to-right scan of the
whole line regardless of quoting, so the lone % paired with %LOG%'s
own opening %, and everything between them (the real replace logic)
was parsed as one bogus, undefined variable name -- silently deleted,
since an undefined %VAR% collapses to empty text inside a batch file.

Fixed by removing every literal % from the -Command text entirely:
$pct = [char]37 builds the percent character inside PowerShell itself,
leaving only the legitimate, correctly-paired %LOG%/%TEMP% reference
on each line. Verified by counting % occurrences on each fully-joined
logical line (exactly 2, the one intended pair, in every case).

Static reasoning about cmd.exe's own %-pairing/substitution semantics
has now been wrong three times in this one code path; the live-cmd.exe
pct_sanitizer fixture is what caught rounds two and three.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Note first real CI evidence for exe_hint_rerun taskkill /T uncertainty

test_hang_after_output_is_ALSO_killed_unlike_exe_smokerun genuinely
timed out on 2/8 lanes (both non-gating) on a commit that touched
neither the test nor tools/exe_hint_rerun.ps1 -- likely CI-runner
timing contention, but recorded per this doc's existing "NOT
CI-confirmed" note since it's the first real signal either way.
Not held pending push -- concurrency.cancel-in-progress would cancel
the in-progress gating-lane runs currently validating the actual fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Update exe_hint_rerun taskkill finding with second CI data point; file Item 27

uv lane's failure (AssertionError: 50.6s not less than 45s) is a
DIFFERENT failure mode of the same UnconditionalKill test than
contract-uv's hard 60s timeout -- two independent runner VMs showing
correlated slowness in the same taskkill /F /T /PID path is enough to
move this from a documentation note to a tracked Active Backlog item
(Item 27), still unrelated to this PR's actual DLL-bundling work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Reset HP_NEXT_DLL/HP_DLL_ITER before dll_bundle_recover's skip branches

CodeRabbit finding, confirmed real: :dll_bundle_recover can run more
than once per process during a REQ-009 cascade re-entry. HP_NEXT_DLL/
HP_DLL_ITER were previously reset only after the skipped_nuitka/
skipped_non_conda early exits, so a stale value left by an earlier
call's successful repair leaked into a later call's skip row via
emit_dll_bundle_row's "$dll = if (\$next) { \$next } else { \$detected }"
fallback. Reset both immediately after the HP_DLL_DETECTED guard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Bound taskkill/WaitForExit waits in exe_hint_rerun.ps1 (Item 27)

The real (gating) lane hit test_exe_hint_rerun.py's UnconditionalKill
test with the same slow-kill signature already seen twice on non-gating
lanes (48.4s vs a 45s assertion). The post-kill sequence had two
genuinely unbounded waits: the blocking `& taskkill.exe` call itself,
and the trailing $p.WaitForExit() with no argument. Both are now bounded
to 5000ms, with taskkill.exe launched via its own Process object instead
of the `&` operator so it can be bounded at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Convert DLL-bundle % sanitizer to an emitted .ps1 helper

The old inline `-Command` sanitization for HP_DLL_DETECTED_SAFE /
HP_NEXT_DLL_SAFE / HP_NEXT_DLL_PATH_SAFE went through three separate
real-CI-confirmed bugs, all rooted in cmd.exe's own %-pairing behavior
on a line containing literal % text. Moved the logic into
tools/dll_pct_sanitize.ps1, a real emitted file invoked via -File --
its body is never parsed by cmd.exe's tokenizer at all, eliminating
the bug class structurally instead of patching around it again.

Also documents the incident and the general rule (prefer -File over
-Command whenever the body needs a literal %, not just embedded ")
in docs/agent-lessons-learned.md and docs/agent-interconnect.md, and
repoints the tests/harness.ps1 live-cmd.exe fixture at the real
mechanism instead of a standalone reproduction of the retired one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Note real-lane confirmation of the exe_hint_rerun taskkill fix

commit f106f78's real lane came back clean (full pytest suite passed,
job conclusion success), confirming the bounded-wait fix actually
resolves the observed CI slowness for the lane that was gating-blocked.
conda-full (the other gating lane) was still running as of this note.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Close Item 27 (both gating lanes confirmed) and align demo doc

f106f78's real and conda-full lanes both came back conclusion:success,
confirming the exe_hint_rerun taskkill fix resolved the CI slowness on
both gating lanes. Item 27 moved from CLAUDE.md's Active Backlog to
docs/agent-closed-backlog.md per the repo's own closure convention.

Also brought docs/demo-bootstrapper-output.md back in line with the
current state of the DLL-bundling repair loop and fixed two stale
"Active Backlog item N" citations that should have pointed at
docs/agent-closed-backlog.md instead, per that doc's own house style.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Fix HP_PY_DIR trailing-backslash argv corruption in DLL-bundle scan

Root cause for self.layered_e2e.chain's mech4Pass staying false (and
plausibly mech3Pass too): HP_PY_DIR (from %~dpI) always ends in one
trailing backslash. Quoted immediately before another quoted argument
at the ~dll_bundle_scan.py call site, that backslash escapes the
closing quote instead of closing it (the same general
CommandLineToArgvW hazard already documented for findstr.exe, here
hitting python.exe's own argv parser) -- silently merging
conda_env_dir with the tried-file argument into garbage, so
locate_dll() always failed even though eccodes.dll is genuinely
present in the real conda-forge package. Deterministic, not flaky --
this fired on every conda-provider run since the loop shipped.

Fixed by doubling the trailing backslash (HP_PY_DIR_ARG) at the one
call site that needed it. Verified via a faithful simulation of the
Windows argv-parsing algorithm, since this hazard can't be reproduced
via a real subprocess on Linux.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* Close Item 24: DLL-bundling fix confirmed via real CI evidence

Commit 45ec269's cache-lane run (31208498606) shows the native-DLL
bundling loop genuinely working for the first time: eccodes.dll was
located under the conda env's Library\bin and bundled into the EXE
(mech4Pass confirmed true). Moved Item 24 to docs/agent-closed-backlog.md
per the repo's own closure convention, and removed the "not yet
confirmed" hedges from agent-interconnect.md and agent-lessons-learned.md.

chainPass itself is still false on that run, but for a reason entirely
outside Item 24's scope: now that the DLL-bundling gap no longer blocks
progress, the EXE gets further and hits a new, deeper gap first --
pygrib's own extension needs numpy and packaging as hidden imports
before colorama's own gap (what mech3 actually checks for) is ever
reached. Filed as a new, separately-scoped Item 28 rather than
reopening Item 24 or guessing at a fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

* docs: address CodeRabbit review findings on test count and pygrib version pin

- Fix stale "34 tests" claim in agent-closed-backlog.md; the file has grown
  to 37 with the HpPyDirArgvQuoting class documented later in the same entry.
- Pin the pygrib Windows-wheel-availability claim in demo-bootstrapper-output.md
  to a specific version+query-date instead of an unversioned "latest release",
  matching CodeRabbit's independently re-verified PyPI JSON API query.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A4iE1BRSkUETwz237XeuTW

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants